package lol.clann.minecraft.springboot.api.model.menu;

import com.ruoshui.utils.java.random.RandomUtils;
import lol.clann.minecraft.springboot.api.bukkit.utils.MenuUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 只允许在主线程操作menu
 * 非主线程操作可能出现并发问题
 *
 * @author: zyp
 * @date: 2020/11/2 3:08 下午
 */
@Getter
@Setter
@ToString
public class Menu {
    private long uid; // 标识
    private MenuUtils menuService;
    private int row;
    private int pageSize;
    private String title;
    /**
     * 所有图标, 若超过一页,会自动分页
     * 一个一个进行填充
     * 没有就是null
     */
    private List<Icon> icons;
    //    <iconUid, Icon>
    private Map<Long, Icon> byUid;
    private MenuClickConcurrentLevelEnum concurrentLevel; // 并发等级
    private int iconSize;  // 图标数量
    private int pages; // 总页数
    private Object lock = new Object();
    //    key: page
    private Map<Integer, Map<Long, InventoryView>> openInventoryMap = new HashMap<>(); // 缓存已打开的menu

    Menu(MenuUtils menuService, int row, String title, MenuClickConcurrentLevelEnum concurrentLevel) {
        this.uid = RandomUtils.nextLong();
        this.menuService = menuService;
        if (row <= 0 || row > 9) {
            throw new IllegalArgumentException("行数应大于0且不大于9. row=" + row);
        }
        this.row = row;
        this.pageSize = row * 9;
        this.title = title;
        this.icons = new ArrayList<>(pageSize);
        this.byUid = new HashMap<>(pageSize);
        this.concurrentLevel = concurrentLevel;
    }

    /**
     * 新建一页
     */
    public void addPage() {
        pages++;
        for (int i = 0; i < pageSize; i++) {
            icons.add(null);
        }
    }

    /**
     * 自动扩容页到指定容量
     *
     * @param contentSize
     */
    public void ensureContentSize(int contentSize) {
        while (getContentSize() < contentSize) {
            addPage();
        }
    }

    /**
     * 移除最后一页
     * 当页数大于1,且最后一页为空时,才能移除
     * 否则报错
     */
    public void removeLastPage() {
        Map<Long, InventoryView> ivs;
        if (pages == 1) {
            throw new RuntimeException("无法移除第一页");
        }
        int to = pages * pageSize;
        int from = to - pageSize;
        for (int i = from; i < to; i++) {
            Icon icon = icons.get(i);
            if (icon != null) {
                throw new RuntimeException("最后一页不为空,无法移除");
            }
        }
//            关闭最后一页
        ivs = openInventoryMap.remove(pages - 1);
        if (ivs != null) {
            for (InventoryView iv : ivs.values()) {
                iv.close();
            }
        }
        pages--;
    }

    /**
     * 获取菜单的空间大小
     *
     * @return
     */
    public int getContentSize() {
        return pageSize * pages;
    }

    /**
     * 设置图标,会更新已打开的gui
     *
     * @param index
     * @param icon
     */
    public void setIcon(int index, Icon icon) {
        int size = getContentSize();
        if (index >= 0 && index < size) {
            icon.index = index;
            Icon replacedIcon = icons.set(index, icon);
            if (replacedIcon != null) {
                byUid.remove(replacedIcon.getUid());
            }
            int page = indexToPage(index);
            Map<Long, InventoryView> ivs = openInventoryMap.get(page);
            if (ivs != null) {
                int pageIndex = indexToPageIndex(index);
                ItemStack showIcon = icon == null ? null : icon.getShowIcon();
                for (InventoryView iv : ivs.values()) {
                    iv.setItem(pageIndex, showIcon);
                }
            }
        } else {
            throw new IndexOutOfBoundsException(size + ":" + index);
        }
    }

    /**
     * 总下标转换成页码
     *
     * @param index
     * @return
     */
    public int indexToPage(int index) {
        return index / pageSize + 1;
    }

    /**
     * 总下标转换成页内下标
     *
     * @param index
     * @return
     */
    public int indexToPageIndex(int index) {
        return index % pageSize;
    }

    public void open(Player player) {
        open(player, 1);
    }

    /**
     * @param player
     * @param page   从0开始
     */
    public void open(Player player, int page) {
        if (page < 0) {
            page = 0;
        }
        if (page >= pages) {
            page = pages - 1;
        }
        int start = page * pageSize - pageSize;
        int to = start + pageSize;
        MenuInventory mi = MenuInventory.create(page, uid, row, title);
        for (int i = start; i < to; i++) {
            Icon icon = icons.get(i);
            if (icon == null) {
                continue;
            }
            if (!icon.canShow(player)) {
                continue; // 没有权限
            }
            mi.setItem(i - start, icon.getShowIcon());
        }
        InventoryView iv = player.openInventory(mi);
        openInventoryMap.computeIfAbsent(page, k -> new HashMap<>()).put(mi.getUid(), iv);
    }

    public void onClick(Player player, InventoryClickEvent event, long iconUid) {
        Icon icon = byUid.get(iconUid);
        if (icon != null) {
            icon.onClick(player, event);
        }
    }

    public void onClose(MenuInventory mi) {
        Map<Long, InventoryView> ivs = openInventoryMap.get(mi.getPage());
        if (ivs != null) {
            ivs.remove(mi.getUid());
        }
    }

    public Icon.IconBuilder createIcon() {
        return new Icon.IconBuilder(this);
    }

    public static class MenuBuilder {
        private MenuUtils menuService;
        private int row;
        private String title;
        private MenuClickConcurrentLevelEnum concurrentLevel;

        public MenuBuilder(MenuUtils menuService) {
            this.menuService = menuService;
        }

        public MenuBuilder row(int row) {
            this.row = row;
            return this;
        }

        public MenuBuilder title(String title) {
            this.title = title;
            return this;
        }

        public MenuBuilder concurrentLevel(MenuClickConcurrentLevelEnum concurrentLevel) {
            this.concurrentLevel = concurrentLevel;
            return this;
        }

        public Menu build() {
            return new Menu(menuService, row, title, concurrentLevel);
        }
    }
}
